{-# LANGUAGE TupleSections #-}

module Wasp.Cli.Command.CreateNewProject.StarterTemplates
  ( StarterTemplate (..),
    TemplateMetadata (..),
    findTemplateByString,
    readWaspProjectSkeletonFiles,
    getTemplateStartingInstructions,
  )
where

import Data.Foldable (find)
import Data.Text (Text)
import StrongPath (Dir', File', Path, Path', Rel, Rel', System, reldir, (</>))
import qualified StrongPath as SP
import qualified Wasp.Cli.GithubRepo as GhRepo
import qualified Wasp.Cli.Interactive as Interactive
import qualified Wasp.Data as Data
import Wasp.Project.Common (WaspProjectDir)
import Wasp.Util.IO (listDirectoryDeep, readFileStrict)
import Wasp.Util.Terminal (styleCode, styleText)

-- More on how starter templates work in Wasp, including the development process,
-- can be found in the `waspc/data/Cli/starters/README.md` file.

data StarterTemplate
  = -- | Template from an archive added to a named GitHub Release as an asset.
    GhRepoReleaseArchiveTemplate
      { repo :: !GhRepo.GithubRepoRef,
        archiveName :: !GhRepo.GithubReleaseArchiveName,
        archivePath :: Path' Rel' Dir',
        metadata :: !TemplateMetadata
      }
  | -- | Template from disk, that comes bundled with wasp CLI.
    BundledStarterTemplate
      { bundledPath :: Path' Rel' Dir',
        metadata :: !TemplateMetadata
      }
  | -- | Template that will be dynamically generated by Wasp AI based on user's input.
    AiGeneratedStarterTemplate

data TemplateMetadata = TemplateMetadata
  { _name :: !String,
    _description :: !String,
    -- | Given the name of the project directory, makes the starting instructions.
    _buildStartingInstructions :: !(String -> String)
  }

instance Show StarterTemplate where
  show (GhRepoReleaseArchiveTemplate {metadata = metadata'}) = _name metadata'
  show (BundledStarterTemplate {metadata = metadata'}) = _name metadata'
  show AiGeneratedStarterTemplate = "ai-generated"

instance Interactive.IsOption StarterTemplate where
  showOption = show

  showOptionDescription (GhRepoReleaseArchiveTemplate {metadata = metadata'}) = Just $ _description metadata'
  showOptionDescription (BundledStarterTemplate {metadata = metadata'}) = Just $ _description metadata'
  showOptionDescription AiGeneratedStarterTemplate =
    Just "🤖 Describe an app in a couple of sentences and have Wasp AI generate initial code for you. (experimental)"

{- HLINT ignore getTemplateStartingInstructions "Redundant $" -}

-- | Returns instructions for running the newly created (from the template) Wasp project.
-- Instructions assume that user is positioned right next to the just created project directory,
-- whose name is provided via projectDirName.
getTemplateStartingInstructions :: String -> StarterTemplate -> String
getTemplateStartingInstructions projectDirName = \case
  GhRepoReleaseArchiveTemplate {metadata = metadata'} -> _buildStartingInstructions metadata' projectDirName
  BundledStarterTemplate {metadata = metadata'} -> _buildStartingInstructions metadata' projectDirName
  AiGeneratedStarterTemplate ->
    unlines
      [ styleText $ "To run your new app, do:",
        styleCode $ "    cd " <> projectDirName,
        styleCode $ "    wasp db migrate-dev",
        styleCode $ "    wasp start"
      ]

findTemplateByString :: [StarterTemplate] -> String -> Maybe StarterTemplate
findTemplateByString templates query = find ((== query) . show) templates

readWaspProjectSkeletonFiles :: IO [(Path System (Rel WaspProjectDir) File', Text)]
readWaspProjectSkeletonFiles = do
  skeletonFilesDir <- (</> [reldir|Cli/starters/skeleton|]) <$> Data.getAbsDataDirPath
  -- FIXME: We filter out the favicon because the program crashes when it tries
  -- to read it as Text in the next line. This means that Wasp AI apps won't
  -- have favicons until we properly fix this.
  -- See the issue for details: https://github.com/wasp-lang/wasp/issues/2951
  skeletonFilePaths <- filter (not . isFavicon) <$> listDirectoryDeep skeletonFilesDir
  mapM (\path -> (path,) <$> readFileStrict (skeletonFilesDir </> path)) skeletonFilePaths
  where
    isFavicon path = SP.fromRelFile (SP.basename path) == "favicon.ico"
